// This file is part of Keepass2Android, Copyright 2025 Philipp Crocoll.
//
//   Keepass2Android is free software: you can redistribute it and/or modify
//   it under the terms of the GNU General Public License as published by
//   the Free Software Foundation, either version 3 of the License, or
//   (at your option) any later version.
//
//   Keepass2Android is distributed in the hope that it will be useful,
//   but WITHOUT ANY WARRANTY; without even the implied warranty of
//   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//   GNU General Public License for more details.
//
//   You should have received a copy of the GNU General Public License
//   along with Keepass2Android.  If not, see <http://www.gnu.org/licenses/>.

using System;
using System.Collections.Generic;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Service.Autofill;
using Android.Util;
using Android.Views;
using Android.Views.Autofill;
using Android.Widget;
using Android.Widget.Inline;
using AndroidX.AutoFill.Inline;
using AndroidX.AutoFill.Inline.V1;
using keepass2android;
using Kp2aAutofillParser;

namespace keepass2android.services.AutofillBase
{
  /// <summary>
  /// This is a class containing helper methods for building Autofill Datasets and Responses.
  /// </summary>
  public class AutofillHelper
  {

    public static InlinePresentation BuildInlinePresentation(InlinePresentationSpec inlinePresentationSpec,
        string text, string subtext, int iconId, PendingIntent pendingIntent, Context context)
    {
      if ((int)Build.VERSION.SdkInt < 30 || inlinePresentationSpec == null)
      {
        return null;
      }
      // If the pending intent is null, we need to create a dummy one to avoid a crash
      pendingIntent ??= PendingIntent.GetActivity(context, 0, new Intent(context, typeof(NullActivity)), PendingIntentFlags.OneShot | PendingIntentFlags.UpdateCurrent | PendingIntentFlags.Immutable);

      var slice = CreateInlinePresentationSlice(
          inlinePresentationSpec,
          text,
          subtext,
          iconId,
          "Autofill option",
          pendingIntent,
          context);
      if (slice != null)
      {
        return new InlinePresentation(slice, inlinePresentationSpec, false);
      }
      return null;
    }


    private static Android.App.Slices.Slice CreateInlinePresentationSlice(
        InlinePresentationSpec inlinePresentationSpec,
        string text,
        string subtext,
        int iconId,
        string contentDescription,
        PendingIntent pendingIntent,
        Context context)
    {
      var imeStyle = inlinePresentationSpec.Style;

      if (!UiVersions.GetVersions(imeStyle).Contains(UiVersions.InlineUiVersion1))
      {
        return null;
      }
      var contentBuilder = InlineSuggestionUi.NewContentBuilder(pendingIntent)
          .SetContentDescription(contentDescription);
      if (!string.IsNullOrWhiteSpace(text))
      {
        contentBuilder.SetTitle(text);
      }
      if (!string.IsNullOrWhiteSpace(subtext))
      {
        contentBuilder.SetSubtitle(subtext);
      }
      if (iconId > 0)
      {
        var icon = Android.Graphics.Drawables.Icon.CreateWithResource(context, iconId);
        if (icon != null)
        {
          if (iconId == AppNames.LauncherIcon)
          {
            // Don't tint our logo
            icon.SetTintBlendMode(Android.Graphics.BlendMode.Dst);
          }
          contentBuilder.SetStartIcon(icon);
        }
      }
      return contentBuilder.Build().JavaCast<InlineSuggestionUi.Content>()?.Slice;
    }

    /// <summary>
    /// Wraps autofill data in a LoginCredential  Dataset object which can then be sent back to the
    /// client View.
    /// </summary>
    public static Dataset NewDataset(Context context,
            AutofillFieldMetadataCollection autofillFields,
            FilledAutofillFieldCollection<ViewNodeInputField> filledAutofillFieldCollection,
            IAutofillIntentBuilder intentBuilder,
            Android.Widget.Inline.InlinePresentationSpec inlinePresentationSpec)
    {
      var datasetName = filledAutofillFieldCollection.DatasetName ?? "[noname]";

      var datasetBuilder = new Dataset.Builder(NewRemoteViews(context.PackageName, datasetName, intentBuilder.AppIconResource));
      datasetBuilder.SetId(datasetName);

      var setValueAtLeastOnce = ApplyToFields(filledAutofillFieldCollection, autofillFields, datasetBuilder);
      AddInlinePresentation(context, inlinePresentationSpec, datasetName, datasetBuilder, intentBuilder.AppIconResource, null);

      if (setValueAtLeastOnce)
      {
        return datasetBuilder.Build();
      }
      /*else
      {
          Kp2aLog.Log("Failed to set at least one value. #fields=" + autofillFields.GetAutofillIds().Length + " " + autofillFields.FocusedAutofillCanonicalHints);
      }*/

      return null;
    }

    /// <summary>
    /// Populates a Dataset.Builder with appropriate values for each AutofillId
    /// in a AutofillFieldMetadataCollection.
    /// 
    /// In other words, it constructs an autofill Dataset.Builder 
    /// by applying saved values (from this FilledAutofillFieldCollection)
    /// to Views specified in a AutofillFieldMetadataCollection, which represents the current
    /// page the user is on.
    /// </summary>
    /// <returns><c>true</c>, if to fields was applyed, <c>false</c> otherwise.</returns>
    /// <param name="filledAutofillFieldCollection"></param>
    /// <param name="autofillFieldMetadataCollection">Autofill field metadata collection.</param>
    /// <param name="datasetBuilder">Dataset builder.</param>
    public static bool ApplyToFields(FilledAutofillFieldCollection<ViewNodeInputField> filledAutofillFieldCollection,
        AutofillFieldMetadataCollection autofillFieldMetadataCollection, Dataset.Builder datasetBuilder)
    {
      bool setValueAtLeastOnce = false;

      foreach (string hint in autofillFieldMetadataCollection.AllAutofillCanonicalHints)
      {
        foreach (AutofillFieldMetadata autofillFieldMetadata in autofillFieldMetadataCollection.GetFieldsForHint(hint))
        {
          FilledAutofillField filledAutofillField;
          if (!filledAutofillFieldCollection.HintMap.TryGetValue(hint, out filledAutofillField) || (filledAutofillField == null))
          {
            continue;
          }

          var autofillId = autofillFieldMetadata.AutofillId;
          var autofillType = autofillFieldMetadata.AutofillType;
          switch (autofillType)
          {
            case AutofillType.List:
              var listValue = autofillFieldMetadata.GetAutofillOptionIndex(filledAutofillField.TextValue);
              if (listValue != -1)
              {
                datasetBuilder.SetValue(autofillId, AutofillValue.ForList(listValue));
                setValueAtLeastOnce = true;
              }
              break;
            case AutofillType.Date:
              var dateValue = filledAutofillField.DateValue;
              datasetBuilder.SetValue(autofillId, AutofillValue.ForDate((long)dateValue));
              setValueAtLeastOnce = true;
              break;
            case AutofillType.Text:
              var textValue = filledAutofillField.TextValue;
              if (textValue != null)
              {
                datasetBuilder.SetValue(autofillId, AutofillValue.ForText(textValue));
                setValueAtLeastOnce = true;
              }
              break;
            case AutofillType.Toggle:
              var toggleValue = filledAutofillField.ToggleValue;
              if (toggleValue != null)
              {
                datasetBuilder.SetValue(autofillId, AutofillValue.ForToggle(toggleValue.Value));
                setValueAtLeastOnce = true;
              }
              break;
            default:
              Log.Warn(CommonUtil.Tag, "Invalid autofill type - " + autofillType);
              break;
          }
        }
      }
      /*
      if (!setValueAtLeastOnce)
      {
          Kp2aLog.Log("No value set. Hint keys : " + string.Join(",", HintMap.Keys));
          foreach (string hint in autofillFieldMetadataCollection.AllAutofillCanonicalHints)
          {
              Kp2aLog.Log("No value set. Hint = " + hint);
              foreach (AutofillFieldMetadata autofillFieldMetadata in autofillFieldMetadataCollection
                  .GetFieldsForHint(hint))
              {
                  Kp2aLog.Log("No value set. fieldForHint = " + autofillFieldMetadata.AutofillId.ToString());
                  FilledAutofillField filledAutofillField;
                  if (!HintMap.TryGetValue(hint, out filledAutofillField) || (filledAutofillField == null))
                  {
                      Kp2aLog.Log("No value set. Hint map does not contain value, " +
                                  (filledAutofillField == null));
                      continue;
                  }

                  Kp2aLog.Log("autofill type=" + autofillFieldMetadata.AutofillType);
              }
          }
      }*/

      return setValueAtLeastOnce;
    }

    public static void AddInlinePresentation(Context context, InlinePresentationSpec inlinePresentationSpec,
        string datasetName, Dataset.Builder datasetBuilder, int iconId, PendingIntent pendingIntent)
    {
      if (inlinePresentationSpec != null)
      {
        var inlinePresentation = BuildInlinePresentation(inlinePresentationSpec, datasetName, "", iconId, pendingIntent, context);
        if (inlinePresentation != null)
          datasetBuilder.SetInlinePresentation(inlinePresentation);
      }
    }

    public static RemoteViews NewRemoteViews(string packageName, string remoteViewsText, int drawableId)
    {
      RemoteViews presentation = new RemoteViews(packageName, Resource.Layout.autofill_service_list_item);
      presentation.SetTextViewText(Resource.Id.text, remoteViewsText);
      presentation.SetImageViewResource(Resource.Id.icon, drawableId);
      return presentation;
    }

    internal static InlinePresentationSpec ExtractSpec(IList<InlinePresentationSpec> inlinePresentationSpecs, int index)
    {
      return inlinePresentationSpecs == null ? null : inlinePresentationSpecs[Math.Min(index, inlinePresentationSpecs.Count - 1)];
    }
  }
}
